/*
 * Copyright (c) 2022  by Junzhao
 * THIS FILE IS PART OF JAVASE Song Huongkang PROJECT
 * All Rights Reserved.
 */

package 处理流.其他流.对象流;

import org.junit.Test;

import java.io.*;

/**
 * @Description 对象流
 * ObjectInputStream 和 ObjectOutputSteam
 *  用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可
 * 以把Java中的对象写入到数据源中，也能把对象从数据源中还原回来。
 * 序列化：用ObjectOutputStream类保存基本类型数据或对象的机制
 * 反序列化：用ObjectInputStream类读取基本类型数据或对象的机制
 * ObjectOutputStream和ObjectInputStream不能序列化static和transient修
 * 饰的成员变量
 * 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流，从
 * 而允许把这种二进制流持久地保存在磁盘上，或通过网络将这种二进制流传
 * 输到另一个网络节点。//当其它程序获取了这种二进制流，就可以恢复成原
 * 来的Java对象
 * 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据，
 * 使其在保存和传输时可被还原
 * 序列化是 RMI（Remote Method Invoke – 远程方法调用）过程的参数和返
 * 回值都必须实现的机制，而 RMI 是 JavaEE 的基础。因此序列化机制是
 * JavaEE 平台的基础
 * 如果需要让某个对象支持序列化机制，则必须让对象所属的类及其属性是可
 * 序列化的，为了让某个类是可序列化的，该类必须实现如下两个接口之一。
 * 否则，会抛出NotSerializableException异常
 * Serializable
 * Externalizable
 * <p>
 * 凡是实现 Serializable 接口的类都有一个表示序列化版本标识符的静态变量：
 * private static final long serialVersionUID;
 * serialVersionUID 用来表明类的不同版本间的兼容性。 简言之，其目的是以序列化对象
 * 进行版本控制，有关各版本反序列化时是否兼容。
 * 如果 类没有显示定义 这个静态 常 量 ，它的值是 Java 运行时环境根据类的内部细节自
 * 动生成的 。 若类的实例变量做了修改 serialVersionUID 可能发生变化。 故 建议，
 * 显式声明。
 * 简单 来说， Java 的序列化机制是通过在运行时判断类的 serialVersionUID 来验
 * 证版本一致性的。在进行反序列化时， JVM 会把传来的字节流中的
 * serialVersionUID 与本地相应 实体类的 serialVersionUID 进行比较，如果相同
 * 就认为是一致的，可以进行反序列化，否则就会出现序列化版本不一致的异
 * 常。 (InvalidCastException)
 * <p><></>
 * 若某个类实现了 Serializable 接口，该类的对象就是可序列化的：
 * 创建一个 ObjectOutputStream
 * 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
 * 注意写出一次，操作flush()一次
 * 反序列化
 * 创建一个 ObjectInputStream
 * 调用 readObject() 方法读取流中的对象
 * 强调：如果某个类的属性不是基本数据类型或 String  类型，而是另一个引用类型，
 * 那么这个引用类型必须是可序列化的，否则拥有该类型的Field 的类也不能序列化
 * <p>
 * 然而有时候，这个类的有些属性需要序列化，而其他属性不需要被序列化，
 * 打个比方，如果一个用户有一些敏感信息（如密码，银行卡号等），为了安全起见，
 * 不希望在网络操作（主要涉及到序列化操作，本地序列化缓存也适用）中被传输，
 * 这些信息对应的变量就可以加上transient关键字。换句话说，
 * 这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
 * <p>
 * 总之，java 的transient关键字为我们提供了便利，你只需要实现Serilizable接口，
 * 将不需要序列化的属性前添加关键字transient，序列化对象的时候，
 * 这个属性就不会序列化到指定的目的地中。
 * @Author 俊昭
 * @Date 2022/4/26
 */
public class ObjectStreamTest {
    public static void main(String[] args) {

    }

    /**
     * 序列化
     * 将内存中的Java对象保存 到磁盘或传输到网络
     * 使用ObjectOutputStream实现
     */
    @Test
    public void objectOutputStreamTest() {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\Junzhao\\IOStream\\对象流\\obj.dat"));

            oos.writeObject(new String("我是大学生"));
            oos.flush();

            oos.writeObject(new Person("yiyi", 17));
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                assert oos != null;
                oos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 反序列化
     * 将保存到磁盘或传输到网络中的Java对象还原 读取到内存中
     * 使用ObjectInputStream实现
     */
    @Test
    public void objectInputStreamTest() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("C:\\Users\\Junzhao\\IOStream\\对象流\\obj.dat"));

            String s = (String) ois.readObject();
            System.out.println(s);

            Person person = (Person) ois.readObject();
            ;
            System.out.println(person);

        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                assert ois != null;
                ois.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }


    }
}

class Person implements Serializable {
    // 自定义类需要实现Serializable接口
    // 并写明UID
    public static final long serialVersionUID = 42123135423L;
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}